/*
 *  Copyright (c) 2022 ZhuHai Jieli Technology Co.,Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "clock.h"
#include "cpu.h"
#include "spinlock.h"
#include "power/p33.h"
#include "efuse.h"
#include "power_interface.h"

#define LOG_TAG_CONST   CLOCK
#define LOG_TAG         "[CLOCK]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#define LOG_DUMP_ENABLE
#include "debug.h"

#ifndef EINVAL
#define EINVAL 22
#endif

/* ----------------------------------------------------------- */
#define log_d printf
#define log_info printf
#define log_error printf
#define log_debug printf

/* ----------------------------------------------------------- */

#define OS_ENTER_CRITICAL()  \
    CPU_CRITICAL_ENTER()

#define OS_EXIT_CRITICAL()  \
    CPU_CRITICAL_EXIT()

__attribute__((noinline))
AT(.volatile_ram_code)
static void __hw_spi_clk_div(u8 baud, u8 cs_cnt)
{
    OS_ENTER_CRITICAL();

    while (!(JL_DSP->CON & BIT(5)));
    while (JL_SFC->CON & BIT(31));
    JL_SFC->CON &= ~BIT(0);
    JL_SFC->BAUD = baud ;
    SFR(JL_SFC->CON, 20, 4, cs_cnt);
    JL_SFC->CON |= BIT(0);

    OS_EXIT_CRITICAL();
}

/*
 *          --------时钟工作模式配置
 *
 *                  P.S 仅内部开发人员配置
 */
///<PLL 差分输入使能
#define PLL_DIFF_ENABLE

///<PLL 后分频APC 固定时钟频率(~-96MHz)
#define APC_Hz            48000000L                // 根据运算能力可调整

///<PLL 后分频ALNK 固定时钟频率(12.288/11.2896MHz)
#define ALNK_Hz            ((192 * 1000000L) / 17)  // 根据运算能力可调整

///<配置外设USB时钟(USB_CLOCK_IN_OSC/USB_CLOCK_IN_PLL48M/USB_CLOCK_IN_LSB)
#define USB_SRC_CLK     USB_CLOCK_IN_PLL48M

///<配置外设Audio时钟(AUDIO_CLOCK_IN_PLL48M/AUDIO_CLOCK_IN_OSC/AUDIO_CLOCK_IN_LSB)
#define AUDIO_SRC_CLK   AUDIO_CLOCK_IN_PLL48M

///<配置外设UART时钟(UART_CLOCK_IN_DISABLE/UART_CLOCK_IN_OSC/UART_CLOCK_IN_PLL48M/UART_CLOCK_IN_LSB)
#define UART_SRC_CLK    UART_CLOCK_IN_PLL48M // UART_CLOCK_IN_OSC

///<配置外设BT时钟(BT_CLOCK_IN_DISABLE/BT_CLOCK_IN_PLL64M/BT_CLOCK_IN_LSB)
#define BT_SRC_CLK      BT_CLOCK_IN_PLL48M

///<配置SFR 时钟(SFR_CLOCK_IDLE/SFR_CLOCK_ALWAYS_ON)
#define SFR_CLK_MODE    SFR_CLOCK_IDLE

#define HSB_CLK_OPTIMIZED       1

///<配置SPI CS#High Time
#define SPI_TSHSL       30

#ifdef SUPPORT_MS_EXTENSIONS
#pragma bss_seg(".clock_bss")
#pragma data_seg(".clock_data")
#pragma const_seg(".clock_const")
#pragma code_seg(".clock_code")
#endif

#define SYS_DVDD_LEVEL(x)   SYSVDD_VOL_SEL(x)

// 16 depend on sys vdd level range
static u32 gpcnt_cnt_table[16];

void core_ring_enable(u8 enable)
{
}

#define MHz_UNIT    1000000L
#define KHz_UNIT    1000L

#define IO_DEBUG_OUT(i, x)       //    {JL_PORT##i->HD &= ~BIT(x), JL_PORT##i->DIR &= ~BIT(x);}

#define GP_IO_DEBUG_0(i, x)    //   {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT &= ~BIT(x);}
#define GP_IO_DEBUG_1(i, x)     //  {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT |= BIT(x);}

#define FREQ_BY_DIV(freq, div)      (freq / (div + 1))

#define RANGE_MAX   4

// SYSVDD 引脚Capless
static const u8 sys_dvdd_tbl[RANGE_MAX] = {
    SYSVDD_VOL_SEL_105V,
    SYSVDD_VOL_SEL_108V,
    SYSVDD_VOL_SEL_114V,
    SYSVDD_VOL_SEL_126V,
};

enum {
    LIMIT_SFC_CLK = 0,
    LIMIT_HSB_CLK,
    LIMIT_LSB_CLK,
    LIMIT_SPI_CLK,
    LIMIT_P33_CLK,
    LIMIT_MAX,
};

static u32 sys_clock_limit[RANGE_MAX][LIMIT_MAX] = {
    // SFC          |  HSB           | LSB          | SPI (FLASH)    | P33
    { 96 * MHz_UNIT,  96 * MHz_UNIT, 96 / 3  * MHz_UNIT, 100 * MHz_UNIT,   32 * MHz_UNIT},
    {120 * MHz_UNIT, 120 * MHz_UNIT, 120 / 3 * MHz_UNIT, 100 * MHz_UNIT,   32 * MHz_UNIT},
    {160 * MHz_UNIT, 160 * MHz_UNIT, 160 / 3 * MHz_UNIT, 100 * MHz_UNIT,   32 * MHz_UNIT},
    {240 * MHz_UNIT, 240 * MHz_UNIT, 240 / 4 * MHz_UNIT, 100 * MHz_UNIT,   32 * MHz_UNIT},
};

static const u32 alnk_clk_tab[] = {
    (192 * MHz_UNIT) / 17,
    (480 * MHz_UNIT) / 39,
};

struct __clock_hdl {
    u8  pll_sys_table;
    u8  pll_sys_div;
    u32 pll_sys_clk;

    u8  alnk_div;
    u32 alnk_clk;
    u32 osc_clk;

    u8  iosc_port;
    u32 iosc_clk;

    u8  sfc_div;
    u32 sfc_clk;

    u16 hsb_div;
    u32 hsb_clk;    // cpu clk

    u8  lsb_div;
    u32 lsb_clk;

    u8  spi_div;
    u8  spi_cs_cnt;
    u32 spi_clk;

    u8 cap_l;
    u8 cap_r;
    u8 ldos;
    u8 hcs12v;

    u8  p33_div;
    u32 p33_clk;

    u8  range;
    u8  voltage_adaptive;
    u8  sys_dvdd;
    u8  sys_dvdd_trim;

    u8  fatal_error;

    u8 pwr_mode;
    u8 vdc13;
    u8 cur_pwr_mode;
};

static struct __clock_hdl hdl = {
    .ldos = 3,
    .hcs12v = 9,
};

#define __this      (&hdl)

// HW Sfr Layer

static void udelay(u32 usec)
{
    u32 prd;

    JL_TIMER0->CON = BIT(14);
    JL_TIMER0->CNT = 0;

    prd = (__this->iosc_clk / MHz_UNIT * usec) / 4;
    JL_TIMER0->PRD = (prd) ? prd : 1; // 1us
    JL_TIMER0->CON = BIT(0) | BIT(3) | BIT(4); // osc clk div 4

    while ((JL_TIMER0->CON & BIT(15)) == 0);

    JL_TIMER0->CON = BIT(14);
}

// wla_con8
#define XOSC_EN_12v_1(x)              (((x) & 0x1) << 0)
#define XOSCLDO_PAS_12v_1(x)          (((x) & 0x1) << 1)
#define XOSCLDO_S_12v_2(x)            (((x) & 0x3) << 2)
#define XOSC_CMP_MODE_12v_1(x)        (((x) & 0x1) << 4)
#define XOSC_CPTEST_EN_12v_1(x)       (((x) & 0x1) << 5)
#define XOSC_HCS_12v_5(x)             (((x) & 0x1f) << 6)
#define XOSC_CLS_12v_4(x)             (((x) & 0xf) << 11)
#define XOSC_CRS_12v_4(x)             (((x) & 0xf) << 15)
#define XOSC_HDS_12v_2(x)             (((x) & 0x3) << 19)
#define XOSC_ANATEST_EN_12v_1(x)      (((x) & 0x1) << 21)
#define XOSC_ANATEST_S_12v_2(x)       (((x) & 0x3) << 22)
#define XOSC_BT_OE_12v_1(x)           (((x) & 0x1) << 24)
#define XOSC_SYS_OE_12v_1(x)          (((x) & 0x1) << 25)
#define XOSC_FM_DAC_OE_12v_1(x)       (((x) & 0x1) << 26)
#define XOSC_CK2XEN_12v_1(x)          (((x) & 0x1) << 27)
#define XOSC_CK2XS_12v_3(x)           (((x) & 0x7) << 28)

// wla_con9
#define XOSC_FSCKOE_12v_1(x)          (((x) & 0x1) << 0)
#define XOSC_FTCS_12v_3(x)            (((x) & 0x7) << 1)
#define XOSC_FTOE_12v_1(x)            (((x) & 0x1) << 4)
#define XOSC_FTIS_12v_4(x)            (((x) & 0xf) << 8)
#define XOSC_FTEN_12v_1(x)            (((x) & 0x1) << 12)

// PLL
#define PLL_EN_12v_1(x)               (((x) & 0x1) << 5)
#define PLL_BIAS_EN_12v_1(x)          (((x) & 0x1) << 6)
#define PLL_RN_12v_1(x)               (((x) & 0x1) << 7)
#define PLL_CLK5G_OE_12v_1(x)         (((x) & 0x1) << 13)
#define PLL_CLK75M_OE_12v_1(x)        (((x) & 0x1) << 14)
#define PLL_IOC_12v_3(x)              (((x) & 0x7) << 15)
#define PLL_IOC_EN_12v_1(x)           (((x) & 0x1) << 18)
#define PLL_DRV_BS_12v_3(x)           (((x) & 0x7) << 19)
#define PLL_IVCOS_12v_2(x)            (((x) & 0x3) << 22)
#define PLL_LOCK_OUTEN_12v_1(x)       (((x) & 0x1) << 24)
#define PLL_LOCK_S_12v_2(x)           (((x) & 0x3) << 25)
#define PLL_VCO_MODE_12v_1(x)         (((x) & 0x1) << 27)

static void __hw_bt_osc_enable(void)
{
    /**********************OSC***********************/

    u32 fmen = JL_ANA->WLA_CON8 & BIT(26);
    JL_ANA->WLA_CON8 =  XOSC_EN_12v_1(1)           |
                        XOSCLDO_PAS_12v_1(0)       |
                        // 点烟器方案写 2 降低对 fm 发射的干扰，如果是耳机或不需要 fm 发射的方案，改为 3
                        XOSCLDO_S_12v_2(__this->ldos)      |
                        XOSC_CMP_MODE_12v_1(0)     |   // CPTEST_EN & CMP_MODE = 00: amp mode
                        XOSC_CPTEST_EN_12v_1(0)    |   // 01: comp mode  10: CP test  11: fast startup ck test
                        XOSC_HCS_12v_5(__this->hcs12v)       |
                        XOSC_CLS_12v_4(0xa)        |
                        XOSC_CRS_12v_4(0xa)        |
                        XOSC_HDS_12v_2(3)          |
                        XOSC_ANATEST_EN_12v_1(0)   |
                        XOSC_ANATEST_S_12v_2(0)    |
                        XOSC_BT_OE_12v_1(1)        |
                        XOSC_SYS_OE_12v_1(1)       |
                        XOSC_FM_DAC_OE_12v_1(0)    |
                        XOSC_CK2XEN_12v_1(0)       |   // syspll  clock  frequency  *2
                        XOSC_CK2XS_12v_3(3)        ;

    if (fmen) {
        JL_ANA->WLA_CON8 |=  XOSC_FM_DAC_OE_12v_1(1);
    }

    JL_ANA->WLA_CON9 =  XOSC_FSCKOE_12v_1(0)       |
                        XOSC_FTCS_12v_3(1)         |
                        XOSC_FTOE_12v_1(0)         |
                        XOSC_FTIS_12v_4(1)         |
                        XOSC_FTEN_12v_1(1)         ;

    clk_set_default_osc_cap();
}

static void __hw_bt_osc_disable(void)
{
    // 关晶振
    JL_ANA->WLA_CON8 &= ~BIT(0);
}

static void __hw_rtoscl_enable(void)
{
}

static void __hw_rtoscl_disable(void)
{
}

static void __hw_rtosch_enable(void)
{
}

static void __hw_rtosh_disable(void)
{
}

static void __hw_rc_enable(void)
{
    RC_EN(1); // RC en
    delay(100);
}

static void __hw_hrc_enable(void)
{
    RCH_EN(1); // RC en
    delay(100);
}

static void __hw_rc_disable(void)
{
    RC_EN(0);
}

static void __hw_lrc_enable(void)
{
}

static void __hw_rcl_enable(void)
{
    p33_tx_1byte(P3_LRC_CON1, lrc_con1_init);
    p33_tx_1byte(P3_LRC_CON0, lrc_con0_init);
    // dpll_out
    JL_CLOCK->CLK_CON2 |= BIT(31);
    p33_or_1byte(P3_CLK_CON0, BIT(2));
}

static void __hw_rch_disable()
{
}

static void __hw_pll_enable(void)
{
    // reset
    log_info("HAL - pll_enable");
    PLL_EN(1);
    delay(10);        // >10us
    PLL_REST(1);    // 复位释放(需在PLL EN使能10us后才能释放)
    delay(10 + 10); // >300us
}

static void __hw_pll_disable(void)
{
    log_info("HAL - pll_disable");
    PLL_EN(0);
    PLL_REST(0); // 复位
}

static void sys_voltage_by_freq(u32 out_freq)
{
    if (__this->pwr_mode == PWR_DCDC15) { // dcdc

        SYSVDD_VOL_SEL(SYSVDD_VOL_SEL_102V);
        if (__this->cur_pwr_mode == PWR_DCDC15) {
            VDC13_VOL_SEL(VDC13_VOL_SEL_105V);
        } else {
            VDC13_VOL_SEL(VDC13_VOL_SEL_125V);
        }
    } else { // ldo

        if (__this->voltage_adaptive) {
            SYSVDD_VOL_SEL(sys_dvdd_tbl[__this->range]);
            VDC13_VOL_SEL(VDC13_VOL_SEL_140V);
        } else {
            SYSVDD_VOL_SEL(__this->sys_dvdd);
            VDC13_VOL_SEL(__this->vdc13);
        }
    }
}

int clk_get(const char *name);
void sys_voltage_reset(u8 pwr_mode)
{
    int clk_sys;
    clk_sys = clk_get("sys");
    __this->cur_pwr_mode = pwr_mode;
    sys_voltage_by_freq(clk_sys);
}

static u8 __hw_clk_limit(u32 frequency, u32 clk_limit, u8 div_max)
{
    u8 div;

    log_info("--func = %s", __FUNCTION__);
    for (div = 0; div <= div_max; div++) {
        if (FREQ_BY_DIV(frequency, div) <= clk_limit) {
            break;
        }
    }

    return div;
}

static u8 __hw_sfc_clk_limit(u32 frequency)
{
    return __hw_clk_limit(frequency, sys_clock_limit[__this->range][LIMIT_SFC_CLK], 7);
}

static u8 __hw_hsb_clk_limit(u32 frequency)
{
    return __hw_clk_limit(frequency, sys_clock_limit[__this->range][LIMIT_HSB_CLK], 255);
}

static u8 __hw_lsb_clk_limit(u32 frequency)
{
    return __hw_clk_limit(frequency, sys_clock_limit[__this->range][LIMIT_LSB_CLK], 7);
}

static u8 __hw_p33_clk_limit(u32 frequency)
{
    return __hw_clk_limit(frequency, sys_clock_limit[__this->range][LIMIT_P33_CLK], 3);
}

static void __hw_p33_clk_div(u8 baud)
{
    SFR(JL_P33->SPI_CON, 2, 2, baud);
}

static u8 __hw_spi_clk_limit(u32 frequency)
{
    return __hw_clk_limit(frequency, sys_clock_limit[__this->range][LIMIT_SPI_CLK], 255);
}

static u8 __hw_spi_cnt_limit(u32 frequency)
{
    return SPI_TSHSL * (frequency / MHz_UNIT) / 1000;
}

// source pll clk ->/ [div] xsb clk / [div] /->sfc clk ->/ [div] /->hsb clk ->/ [div] /-> lsb clk
//                                               |
//                                               |
//                                               ->/ [div] / -> spi clk
static void clock_all_limit_pre(u32 source_clk, u32 frequency)
{
    u8 div;

    ASSERT(frequency, "%s", __func__);

    div = __hw_sfc_clk_limit(source_clk);
    __this->sfc_div = div;
    __this->sfc_clk = FREQ_BY_DIV(source_clk, div);

    __this->spi_cs_cnt = __hw_spi_cnt_limit(__this->sfc_clk);

    div = __hw_spi_clk_limit(__this->sfc_clk);
    __this->spi_div = div;
    __this->spi_clk = FREQ_BY_DIV(__this->sfc_clk, div);

    if (frequency != 0) {
        div = __this->sfc_clk / frequency;
    }
    if (div) {
        div -= 1;
    }
    div = MAX(div, __hw_hsb_clk_limit(__this->sfc_clk));
    __this->hsb_div = div;
    __this->hsb_clk = FREQ_BY_DIV(__this->sfc_clk, div);

    frequency = __this->hsb_clk;
    div = __hw_lsb_clk_limit(frequency);
    __this->lsb_div = div;
    __this->lsb_clk = FREQ_BY_DIV(frequency, div);

    frequency = __this->lsb_clk;
    div = __hw_p33_clk_limit(frequency);
    __this->p33_div = div;
    __this->p33_clk = FREQ_BY_DIV(frequency, div);
}

static void __hw_set_spi_div(u8 div)
{
    u32 frequency;
    u8 limit;

    if (div == 0xff) {
        return;
    }

    frequency   = __this->sfc_clk;
    limit       = __hw_spi_clk_limit(frequency);
    if (div >= limit) {
        __this->spi_div = div;
        __this->spi_clk = FREQ_BY_DIV(frequency, div);
    } else {
        log_error("SPI div overlimit : %d < %d", div, limit);
        __this->spi_div = limit;
        __this->spi_clk = FREQ_BY_DIV(frequency, limit);
    }
}

static void __hw_set_hsb_div(u8 div)
{
    u32 frequency;
    u8 limit;

    if (div == 0xff) {
        return;
    }

    frequency   = __this->sfc_clk;
    limit       = __hw_hsb_clk_limit(frequency);
    if (div >= limit) {
        __this->hsb_div = div;
        __this->hsb_clk = FREQ_BY_DIV(frequency, div);
    } else {
        log_error("HSB div overlimit : %d < %d", div, limit);
        __this->hsb_div = limit;
        __this->hsb_clk = FREQ_BY_DIV(frequency, limit);
    }
}

static void __hw_set_lsb_div(u8 div)
{
    u32 frequency;
    u8 limit;

    if (div == 0xff) {
        return;
    }

    frequency   = __this->hsb_clk;
    limit       = __hw_lsb_clk_limit(frequency);
    if (div >= limit) {
        __this->lsb_div = div;
        __this->lsb_clk = FREQ_BY_DIV(frequency, div);
    } else {
        log_error("LSB div overlimit : %d < %d lsb_clk:%d", div, limit);
        __this->lsb_div = limit;
        __this->lsb_clk = FREQ_BY_DIV(frequency, limit);
    }
}

static void __hw_set_p33_div(void)
{
    u32 frequency;
    u8 limit;

    frequency   = __this->lsb_clk;
    limit       = __hw_p33_clk_limit(frequency);

    __this->p33_div = limit;
    __this->p33_clk = FREQ_BY_DIV(frequency, limit);
}

static void clock_tree_all_limit_pre(u32 sfc_clk, u8 hsb_div, u8 lsb_div, u8 spi_div)
{
    u8 div;

    ASSERT(sfc_clk, "%s", __func__);

    div = __hw_sfc_clk_limit(sfc_clk);
    __this->sfc_div = div;
    __this->sfc_clk = FREQ_BY_DIV(sfc_clk, div);

    // only new div under limit div
    __hw_set_hsb_div(hsb_div);

    __hw_set_lsb_div(lsb_div);

    __hw_set_spi_div(spi_div);

    __hw_set_p33_div();
}

static void clock_all_limit_post(void)
{
    if ((__this->pll_sys_clk != -1)
        || (__this->osc_clk != 1)) {

        SFC_CLK_DIV(__this->sfc_div);
        __hw_spi_clk_div(__this->spi_div, __this->spi_cs_cnt);
        HSB_CLK_DIV(__this->hsb_div);
        LSB_CLK_DIV(__this->lsb_div);
    }
}

static void __hw_clock_debug_PB8(u8 out)
{
    if (out == PB8_CLOCK_OUTPUT) {
        PB8_CLOCK_OUT(PB8_CLOCK_OUTPUT);
    } else {
        PB8_CLOCK_OUT(out);
        IO_DEBUG_OUT(B, 8);
    }
}

static void __hw_clock_debug_PA0(u8 out)
{
    if (out == PA0_CLOCK_OUTPUT) {
        PA0_CLOCK_OUT(PA0_CLOCK_OUTPUT);
    } else {
        PA0_CLOCK_OUT(out);
        IO_DEBUG_OUT(A, 0);
    }
}

static BOOL __hw_pll_sys_clk_out_pre(u32 frequency)
{
    u8 i, j, k;

    log_info("--func = %s", __FUNCTION__);
    log_info("pll sys frequency : 0x%d", frequency);

    u8 div0_tbl[] = {1, 2, 4, 8};
    u8 div1_tbl[] = {1, 3, 5, 7};
    u32 pll_clk_tbl[] = {
        192 * MHz_UNIT,
        137 * MHz_UNIT,
        320 * MHz_UNIT,
        480 * MHz_UNIT,
    };
    u32 pll_clk, target_clk;
    u8 div0, div1;

    for (i = 0; i < ARRAY_SIZE(pll_clk_tbl); i++) {
        pll_clk = pll_clk_tbl[i];
        for (j = 0; j < ARRAY_SIZE(div0_tbl); j++) {
            div0 = div0_tbl[j];
            for (k = 0; k < ARRAY_SIZE(div1_tbl); k++) {
                div1 = div1_tbl[k];
                target_clk = pll_clk / div0 / div1;
                if (frequency == target_clk) {
                    __this->pll_sys_table = i;
                    __this->pll_sys_div = j * 4 + k;
                    log_info("pll_tbl : %d - %d - %d", i, j, k);
                    __this->pll_sys_clk = target_clk;
                    return TRUE;
                }
            }
        }
    }

    __this->pll_sys_clk = -1;
    return FALSE;
}

static void __hw_pll_sys_clk_out_post(void)
{
    if (__this->pll_sys_clk == -1) {
        return;
    }

    PLL_SYS_SEL(__this->pll_sys_table);
    PLL_SYS_DIV(__this->pll_sys_div);
}

static BOOL __hw_alnk_clk_out_pre(u32 frequency)
{
    u8 index;

    log_info("--func = %s", __FUNCTION__);
    log_info(" alnk frequency : 0x%d", frequency);

    for (index = 0; index < ARRAY_SIZE(alnk_clk_tab); index++) {
        if (frequency == alnk_clk_tab[index]) {
            __this->alnk_div = index;
            log_info(" alnk_clk_tab : 0x%d", index);
            __this->alnk_clk = alnk_clk_tab[index];
            return TRUE;
        }
    }

    __this->alnk_clk = -1;
    return FALSE;
}

static void __hw_alnk_clk_out_post(void)
{
    if (__this->alnk_clk == -1) {
        return;
    }

    PLL_ALNK_EN(1);
    PLL_ALNK_SEL(__this->alnk_div);
}

static void __hw_pll_all_oe(void)
{
    BOOL pll_clk480_oe = 0;

    pll_clk480_oe |= ((JL_CLOCK->CLK_CON2 & (BIT(0) | BIT(1))) == PLL_SYS_SEL_PLL480M) ? 1 : 0;
    pll_clk480_oe |= (((JL_CLOCK->CLK_CON2 & (BIT(30))) >> 30) == PLL_ALNK_480M_DIV39) ? 1 : 0;

    BOOL pll_clk320_oe = 0;

    pll_clk320_oe |= ((JL_CLOCK->CLK_CON2 & (BIT(0) | BIT(1))) == PLL_SYS_SEL_PLL320M) ? 1 : 0;

    BOOL pll_clk137_oe = 0;

    pll_clk137_oe |= ((JL_CLOCK->CLK_CON2 & (BIT(0) | BIT(1))) == PLL_SYS_SEL_PLL137M) ? 1 : 0;
    pll_clk137_oe |= ((JL_CLOCK->CLK_CON2 & (BIT(18) | BIT(19)) >> 18) == PLL_APC_SEL_PLL137M) ? 1 : 0;

    BOOL pll_clk107_oe = 0;

    pll_clk107_oe |= ((JL_CLOCK->CLK_CON2 & (BIT(18) | BIT(19)) >> 18) == PLL_APC_SEL_PLL137M) ? 1 : 0;

    pll_clk107_oe = 1;
    PLL_CLK107M_OE(pll_clk107_oe);
    pll_clk137_oe = 1;
    PLL_CLK137M_OE(pll_clk137_oe);
    // for USB always output
    PLL_CLK192M_OE(1);
    pll_clk320_oe = 1;
    PLL_CLK320M_OE(pll_clk320_oe);
    // for LRC capture always output
    pll_clk480_oe = 1;
    PLL_CLK480M_OE(pll_clk480_oe);
}

static void __hw_pll_run_pre(u32 input_freq, u32 out_freq)
{
    __hw_pll_sys_clk_out_pre(out_freq);

    __hw_alnk_clk_out_pre(ALNK_Hz);
}

static void __hw_pll_btosc_run_post(u32 input_freq)
{
    u32 frequency;

    log_info("--func = %s", __FUNCTION__);
    log_info("PLL in freq : 0x%d", input_freq);
    /****************Analog**********************/
    PLL_PFD(1);
    PLL_ICP(5);
    PLL_LPFR2(2);

    PLL_LDO_BYPASS(0);
    PLL_LDO12A(3);
    PLL_IVCO(3);
    /****************Analog end**********************/

    // <关闭 PLL
    __hw_pll_disable();

    // <设置 PLL 参考源和参考频率
    // Fout = Fref / DIVn * DIVm
    ASSERT((input_freq == 12 * MHz_UNIT) || (input_freq == 24 * MHz_UNIT), "%s\n, __func__");
    if (input_freq == 12 * MHz_UNIT) {
        PLL_DIVn_EN(PLL_DIVn_DIS_DIV1);
    } else {
        PLL_DIVn_EN(PLL_DIVn_EN2_33);
    }
    PLL_DIVn(0);    // DIVn
    PLL_FBDS((480 / 12) - 2);  // DIVm

    // <设置 PLL 后分频系数
    __hw_pll_sys_clk_out_post();

    __hw_alnk_clk_out_post();

    __hw_pll_all_oe();

    // <使能 PLL
    __hw_pll_enable();
}

static void __hw_pll_rcl_run_post(u32 input_freq)
{
    JL_CLOCK->PLL_CON = lrc_pll_con;

    u32 efuse_page1_val = p33_rd_page(1);
    u32 diff = (efuse_page1_val >> 22) & 0x3ff;
    if (diff == 0x3ff) {
        diff = 0;
    }
    if (diff & BIT(9)) {
        diff &= 0x1ff;
        JL_CLOCK->PLL_CON1 = lrc_pll_con1 - diff;
    } else {
        JL_CLOCK->PLL_CON1 = lrc_pll_con1 + diff;
    }

    // 关晶振
    __hw_bt_osc_disable();

    __hw_pll_sys_clk_out_post();

    __hw_alnk_clk_out_post();

    __hw_pll_all_oe();

    // <使能 PLL
    __hw_pll_enable();
}

// XOSC FSCK -> bt_osc
//          |-> System
static u8 cap, current;

static void __hw_xosc_fsck_run(void)
{
    // auto switch

    // RC En
    JL_ANA->WLA_CON9 |= BIT(12);

    // 设置闸门时间 = 32 * 2^16 / 16000000 =
    GPCNT_CLR_PEND(1);
    // [12:14]
    GPCNT_GSS(GPCNT_GSS_OSC);
    GPCNT_CSS(GPCNT_CSS_PLL480M);
    GPCNT_GTS(3);

    GPCNT_CLR_PEND(1);
    GPCNT_EN(1);

    while (!(JL_GPCNT->CON & BIT(7)));

    // CNT0 / 480 000000 = time = 32 * 2^3 / xosc
    // xosc = 32 * 2^3 * 480 000000 / CNT0
    int xosc = (32 * 8 * 480 * KHz_UNIT) / JL_GPCNT->NUM;

    log_debug("JL_GPCNT->NUM : 0x%x", JL_GPCNT->NUM);
    log_debug("XOSC %d(KHz)", xosc);

    GPCNT_EN(0);
}

static void xosc_fsck_trim(void)
{
    int clk_con1 = JL_CLOCK->CLK_CON0;

    OSC_CLOCK_IN(OSC_CLOCK_IN_XOSC_FSCK);

    SFR(JL_ANA->WLA_CON9, 4, 1, 0);
    // System OE
    JL_ANA->WLA_CON9 |= BIT(0); // FSCKOE

    // btosc OE

    log_debug("JL_ANA->WLA_CON9 0x%x", JL_ANA->WLA_CON9);
    SFR(JL_ANA->WLA_CON9, 1, 3, 5);
    SFR(JL_ANA->WLA_CON9, 8, 3, 3);

    SFR(JL_ANA->WLA_CON9, 4, 1, 0);

    __hw_xosc_fsck_run();

    OSC_CLOCK_IN((clk_con1 >> 4) & 0x3);
    log_debug("CLK_CON0 : %08x",       JL_CLOCK->CLK_CON0);
}

// HW Abstract Layer
static void sys_internal_clock_by_osc(SYS_ICLOCK_INPUT input, u32 input_freq)
{
    switch (input) {
        case SYS_ICLOCK_INPUT_BTOSC:
            __hw_bt_osc_enable();
            OSC_CLOCK_IN(OSC_CLOCK_IN_BT_OSC);
            break;
        case SYS_ICLOCK_INPUT_RTOSCL:
            __hw_rtoscl_enable();
            OSC_CLOCK_IN(OSC_CLOCK_IN_RTOSC_L);
            break;
        case SYS_ICLOCK_INPUT_PAT:
            OSC_CLOCK_IN(OSC_CLOCK_IN_PAT);
            break;
    }

    __this->iosc_port = input;
    __this->iosc_clk = input_freq;
}

static void sys_clock_by_osc_pre(u8 input, u32 input_freq, u32 out_freq)
{
    u16 div;
    if (out_freq != 0) {
        div = input_freq % out_freq;
        if (!div) {
            div = (input_freq / out_freq) - 1;
            if (div <= 256) {
                __this->osc_clk = out_freq;
                clock_all_limit_pre(__this->osc_clk, __this->osc_clk);
                return;
            }
        }

        __this->osc_clk = -1;
    }
    return;
}

static void sys_clock_by_osc_post(u8 input, u32 input_freq, u32 out_freq)
{
    clock_all_limit_post();

    switch (input) {
        case SYS_CLOCK_INPUT_BT_OSC:
            __hw_bt_osc_enable();
            MAIN_CLOCK_SEL(MAIN_CLOCK_IN_BTOSC);
            break;
        case SYS_CLOCK_INPUT_RTOSCL:
            __hw_rtoscl_enable();
            MAIN_CLOCK_SEL(MAIN_CLOCK_IN_RTOSC_L);
            break;
        case SYS_CLOCK_INPUT_PAT:
            TEST_SEL(1);
            break;
        default:
            break;
    }
}

static u32 pll_clock_by_all_limit(u32 out_freq)
{
    u32 sfc_clk_limit = sys_clock_limit[__this->range][LIMIT_SFC_CLK];
    u32 spi_clk_limit = sys_clock_limit[__this->range][LIMIT_SPI_CLK];
    u32 hsb_clk_limit = sys_clock_limit[__this->range][LIMIT_HSB_CLK];
    u32 pll_max_clk;

    if (out_freq > sfc_clk_limit) {
        return -1;
    }

    if (out_freq > hsb_clk_limit) {
        return -1;
    }

    pll_max_clk = MAX(spi_clk_limit, out_freq);

    pll_max_clk = MIN(pll_max_clk, sfc_clk_limit);

    if ((pll_max_clk != 0) && (out_freq != 0)) {
        pll_max_clk =
            (out_freq > pll_max_clk) ? (out_freq / pll_max_clk) * out_freq : (pll_max_clk / out_freq) * out_freq;
    }
    return pll_max_clk;
}

static void sys_clock_by_pll_pre(u8 input, u32 input_freq, u32 out_freq)
{
#if (HSB_CLK_OPTIMIZED)
    __this->pll_sys_clk = pll_clock_by_all_limit(out_freq);

    if (__this->pll_sys_clk == -1) {
        return;
    }
#else
    __this->pll_sys_clk = out_freq;
#endif

    __hw_pll_run_pre(input_freq, __this->pll_sys_clk);

    if (__this->pll_sys_clk == -1) {
        return;
    }

    clock_all_limit_pre(__this->pll_sys_clk, out_freq);
}

static void sys_clock_by_pll_post(u8 input, u32 input_freq, u32 out_freq)
{
    switch (input) {
        case SYS_CLOCK_INPUT_PLL_BT_OSC:
            __hw_bt_osc_enable();
#ifdef PLL_DIFF_ENABLE
            PLL_RSEL(0);
#else
            PLL_REF_SEL(PLL_REF_SEL_BTOSC);
#endif
            __hw_pll_btosc_run_post(input_freq);
            break;
        case SYS_CLOCK_INPUT_PLL_RCL:
            __hw_rcl_enable();
            PLL_REF_SEL1(PLL_RSEL_RCLK);
            PLL_REF_SEL(PLL_REF_SEL_RCLK);
            __hw_pll_rcl_run_post(input_freq);
            break;
        case SYS_CLOCK_INPUT_PLL_RCH:
            ASSERT("-------To do");
            PLL_REF_SEL1(PLL_RSEL_RCH);
            PLL_REF_SEL(PLL_REF_SEL_RCLK);
            break;
        case SYS_CLOCK_INPUT_PLL_PAT:
            PLL_REF_SEL(PLL_REF_SEL_RCLK);
            __hw_pll_btosc_run_post(input_freq);
            break;
        default:
            break;
    }

    clock_all_limit_post();

    MAIN_CLOCK_SEL(MAIN_CLOCK_IN_PLL);
}

static void voltage_by_freq_pre(u32 frequency)
{
    if (__this->voltage_adaptive == 0) {
        __this->range = RANGE_MAX - 1;
        return;
    }

    int i;

    for (i = 0; i < RANGE_MAX; i++) {
        if (frequency <= sys_clock_limit[i][LIMIT_HSB_CLK]) {
            break;
        }
    }

    __this->range = (i == RANGE_MAX) ? RANGE_MAX - 1 : i;
}

static void voltage_by_freq_post(u32 frequency)
{
    sys_voltage_by_freq(frequency);
}

static void sys_clock_by_pll_re_run(u32 frequency)
{
    voltage_by_freq_pre(frequency);

#if (HSB_CLK_OPTIMIZED)
    __this->pll_sys_clk = pll_clock_by_all_limit(frequency);

    if (__this->pll_sys_clk == -1) {
        log_error("frequency over limit");
        return;
    }
#else
    __this->pll_sys_clk = frequency;
#endif

    __hw_pll_sys_clk_out_pre(__this->pll_sys_clk);

    if (__this->pll_sys_clk == -1) {
        log_error("No frequency table match");
        return;
    }

    clock_all_limit_pre(__this->pll_sys_clk, frequency);

    OS_ENTER_CRITICAL();

    TEST_SEL(0);

    switch (__this->iosc_port) {
        case SYS_ICLOCK_INPUT_BTOSC:
            log_info("SYS_ICLOCK_INPUT_BTOSC");
            MAIN_CLOCK_SEL(MAIN_CLOCK_IN_BTOSC);
            break;
        default :
            __hw_hrc_enable();
            MAIN_CLOCK_SEL(MAIN_CLOCK_IN_RC);
            break;
    }

    voltage_by_freq_post(frequency);

    __hw_pll_sys_clk_out_post();

    __hw_pll_all_oe();

    clock_all_limit_post();

    MAIN_CLOCK_SEL(MAIN_CLOCK_IN_PLL);

    OS_EXIT_CRITICAL();
}

static void sys_clock_tree_by_pll_re_run(u32 sfc_clk, u8 hsb_div, u8 lsb_div, u8 spi_div)
{
    voltage_by_freq_pre(sfc_clk);

    __hw_pll_sys_clk_out_pre(sfc_clk);

    if (__this->pll_sys_clk == -1) {
        log_error("No frequency table match");
        return;
    }

    clock_tree_all_limit_pre(__this->pll_sys_clk, hsb_div, lsb_div, spi_div);

    OS_ENTER_CRITICAL();

    TEST_SEL(0);

    switch (__this->iosc_port) {
        case SYS_ICLOCK_INPUT_BTOSC:
            log_info("SYS_ICLOCK_INPUT_BTOSC");
            MAIN_CLOCK_SEL(MAIN_CLOCK_IN_BTOSC);
            break;
        default :
            __hw_hrc_enable();
            MAIN_CLOCK_SEL(MAIN_CLOCK_IN_RC);
            break;
    }

    voltage_by_freq_post(sfc_clk);

    __hw_pll_sys_clk_out_post();

    __hw_pll_all_oe();

    clock_all_limit_post();

    MAIN_CLOCK_SEL(MAIN_CLOCK_IN_PLL);

    OS_EXIT_CRITICAL();
}

static void clock_run(u8 input, u32 input_freq, u32 out_freq)
{
    voltage_by_freq_pre(out_freq);

    // pre
    switch (input) {
        // <- (原生时钟源)
        case SYS_CLOCK_INPUT_BT_OSC:
        case SYS_CLOCK_INPUT_RTOSCL:
        case SYS_CLOCK_INPUT_PAT:
            sys_clock_by_osc_pre(input, input_freq, out_freq);
            break;
        // <- (衍生时钟源)
        case SYS_CLOCK_INPUT_PLL_RCL:
        case SYS_CLOCK_INPUT_PLL_RCH:
        case SYS_CLOCK_INPUT_PLL_BT_OSC:
        case SYS_CLOCK_INPUT_PLL_PAT:
            sys_clock_by_pll_pre(input, input_freq, out_freq);
            break;
        case SYS_CLOCK_INPUT_RC:
            break;
    }

    OS_ENTER_CRITICAL();

    TEST_SEL(0);

    // current using OSC / PLL for system clock
    __hw_hrc_enable();

    MAIN_CLOCK_SEL(MAIN_CLOCK_IN_RC);

    voltage_by_freq_post(out_freq);

    // post
    switch (input) {
        // <- (原生时钟源)
        case SYS_CLOCK_INPUT_BT_OSC:
        case SYS_CLOCK_INPUT_RTOSCL:
        case SYS_CLOCK_INPUT_PAT:
            sys_clock_by_osc_post(input, input_freq, out_freq);
            break;
        // <- (衍生时钟源)
        case SYS_CLOCK_INPUT_PLL_RCL:
        case SYS_CLOCK_INPUT_PLL_RCH:
        case SYS_CLOCK_INPUT_PLL_BT_OSC:
        case SYS_CLOCK_INPUT_PLL_PAT:
            sys_clock_by_pll_post(input, input_freq, out_freq);
            break;
        default:
            ASSERT((input == SYS_CLOCK_INPUT_RC), "%s", __func__);
            break;
    }

    OS_EXIT_CRITICAL();
}

/********************************************************************************/
/*
 *                   API Layer
 */
void hw_clock_switching(u32 out_freq)
{
    struct clock_critical_handler *p;

    list_for_each_loop_clock_critical(p) {
        p->enter();
    }

    if (__this->pll_sys_clk) {
        sys_clock_by_pll_re_run(out_freq);
    }

    list_for_each_loop_clock_critical(p) {
        p->exit();
    }
}

void hw_clock_tree_switching(u32 sfc_clk, u8 hsb_div, u8 lsb_div, u8 spi_div)
{
    if (__this->pll_sys_clk) {
        sys_clock_tree_by_pll_re_run(sfc_clk, hsb_div, lsb_div, spi_div);
    }
    // echo new clock tree
}

// for MACRO - RMODE
enum {
    NORMAL_READ = 0,
    FAST_READ,
    DUAL_FAST_READ_OUTPUT,       // CMD 1 WIRE, ADDR 1 WIRE
    QUAD_FAST_READ_OUTPUT,       // CMD 1 WIRE, ADDR 1 WIRE
    DUAL_FAST_READ_IO,           // CMD 1 WIRE, ADDR 2 WIRE
    QUAD_FAST_READ_IO,           // CMD 1 WIRE, ADDR 4 WIRE
    NO_SEND_COMMAND_DUAL_MODE,   // ADDR 2 WIRE
    NO_SEND_COMMAND_QUAD_MODE,   // ADDR 4 WIRE
    DUAL_WIRE_SEND_COMMAND_MODE, // CMD 2 WIRE, ADDR 2 WIRE
    QUAD_WIRE_SEND_COMMAND_MODE, // CMD 4 WIRE, ADDR 4 WIRE
};
static u32 _quad_max_freq = 50 * MHz_UNIT;
void clock_set_sfc_max_freq(u32 dual_max_freq, u32 quad_max_freq)
{
    for (u8 i = 0; i < RANGE_MAX; i++) {
        sys_clock_limit[i][LIMIT_SPI_CLK] = dual_max_freq ;
    }
    _quad_max_freq = quad_max_freq;
}
void clock_reset_lsb_max_freq(u32 max_freq)
{
    sys_clock_limit[RANGE_MAX - 1][LIMIT_LSB_CLK] = max_freq ;
}
static void adaptive_frequency_limit(void)
{
    u8 code;
    u8 wire4 = 0;

    code = ((JL_SFC->CON & 0xf00)) >> 8;

    log_info("SFC READMODE : 0x%x", code);
    switch (code) {
        case NORMAL_READ:
            break;
        case FAST_READ:
            break;
        case DUAL_FAST_READ_OUTPUT:
            break;
        case QUAD_FAST_READ_OUTPUT:
            wire4 = 1;
            break;
        case DUAL_FAST_READ_IO:
            break;
        case QUAD_FAST_READ_IO:
            wire4 = 1;
            break;
        case NO_SEND_COMMAND_DUAL_MODE:
            break;
        case NO_SEND_COMMAND_QUAD_MODE:
            wire4 = 1;
            break;
        case DUAL_WIRE_SEND_COMMAND_MODE:
            break;
        case QUAD_WIRE_SEND_COMMAND_MODE:
            wire4 = 1;
            break;
    }

    if (wire4) {
        for (u8 i = 0; i < RANGE_MAX; i++) {
            sys_clock_limit[i][LIMIT_SPI_CLK] = _quad_max_freq ;
        }
    }
}

void hw_clock_init(u8 sys_in, u32 input_freq, u32 out_freq)
{
    adaptive_frequency_limit();

    __this->sys_dvdd_trim = get_sysdvdd_trim();

    switch (sys_in) {
        // <- (原生时钟源)
        case SYS_CLOCK_INPUT_RTOSCL:
            sys_internal_clock_by_osc(SYS_ICLOCK_INPUT_RTOSCL, input_freq);
            break;
        // <- (衍生时钟源)
        case SYS_CLOCK_INPUT_BT_OSC:
        case SYS_CLOCK_INPUT_PLL_BT_OSC:
            sys_internal_clock_by_osc(SYS_ICLOCK_INPUT_BTOSC, input_freq);
            break;
        case SYS_CLOCK_INPUT_PAT:
        case SYS_CLOCK_INPUT_PLL_PAT:
            sys_internal_clock_by_osc(SYS_ICLOCK_INPUT_PAT, input_freq);
            break;
        default:
            __this->iosc_port = 0xff;
            break;
    }

    clock_run(sys_in, input_freq, out_freq);   // run clock

    // Additional - Default
    UART_CLOCK_IN(UART_SRC_CLK);
}

u32 clock_get_usb_freq(void)
{
    u32 frequency = 0;

    switch (USB_SRC_CLK) {
        case USB_CLOCK_IN_PLL48M:
            frequency = (__this->pll_sys_clk == 0) ? 0 : 48 * MHz_UNIT;
            break;
        case USB_CLOCK_IN_OSC:
            frequency = __this->iosc_clk;
            break;
        case USB_CLOCK_IN_LSB:
            frequency = __this->lsb_clk;
            break;
    }

    return frequency;
}

u32 clock_get_audio_freq(void)
{
    u32 frequency = 0;

    switch (AUDIO_SRC_CLK) {
        case AUDIO_CLOCK_IN_PLL48M:
            frequency = (__this->pll_sys_clk == 0) ? 0 : 48 * MHz_UNIT;
            break;
        case AUDIO_CLOCK_IN_OSC:
            frequency = __this->iosc_clk;
            break;
        case AUDIO_CLOCK_IN_LSB:
            frequency = __this->lsb_clk;
            break;
    }

    return frequency;
}

u32 clock_get_uart_freq(void)
{
    u32 frequency = 0;

    switch (UART_SRC_CLK) {
        case UART_CLOCK_IN_PLL48M:
            frequency = (__this->pll_sys_clk == 0) ? 0 : 48 * MHz_UNIT;
            break;
        case UART_CLOCK_IN_OSC:
            frequency = __this->iosc_clk;
            break;
        case UART_CLOCK_IN_LSB:
            frequency = __this->lsb_clk;
            break;
    }

    return frequency;
}

u32 clock_get_bt_freq(void)
{
    u32 frequency = 0;

    switch (BT_SRC_CLK) {
        case BT_CLOCK_IN_PLL48M:
            frequency = (__this->pll_sys_clk == 0) ? 0 : 48 * MHz_UNIT;
            break;
        case BT_CLOCK_IN_HSB:
            frequency = __this->hsb_clk;
            break;
        case BT_CLOCK_IN_LSB:
            frequency = __this->lsb_clk;
            break;
    }

    return frequency;
}

u32 clock_get_sfc_freq(void)
{
    return __this->sfc_clk;
}

u32 clock_get_hsb_freq(void)
{
    return __this->hsb_clk;
}

u32 clock_get_lsb_freq(void)
{
    return __this->lsb_clk;
}

u32 clock_get_iosc_freq(void)
{
    return __this->iosc_clk;
}

u32 clock_get_osc_freq(void)
{
    return __this->osc_clk;
}

u32 clock_get_spi_freq(void)
{
    return __this->spi_clk;
}

void clock_dump(void)
{
    log_debug("[-------------Clock Dump-----------]\n");
    ASSERT(__this->fatal_error == 0, "Fatal Error");

    log_debug("---CHIP Ver. : 0x%x",    JL_SYSTEM->CHIP_ID);
    log_debug("--Internal OSC CLK : %d", __this->iosc_clk);
    log_debug("--OSC CLK : %d",        __this->osc_clk);

    switch (__this->pll_sys_table) {
        case PLL_SYS_SEL_PLL137M:
            log_info("--PLL SYS SEL : PLL_SYS_SEL_PLL137M\n");
            break;
        case PLL_SYS_SEL_PLL192M:
            log_info("--PLL SYS SEL : PLL_SYS_SEL_PLL192M\n");
            break;
        case PLL_SYS_SEL_PLL320M:
            log_info("--PLL SYS SEL : PLL_SYS_SEL_PLL320M\n");
            break;
        case PLL_SYS_SEL_PLL480M:
            log_info("--PLL SYS SEL : PLL_SYS_SEL_PLL480M\n");
            break;
    }
    log_debug("--PLL SYS CLK : %d\n",    __this->pll_sys_clk);

    log_debug("--PLL ALNK CLK : %d\n",   __this->alnk_clk);

    log_debug("---SFC CLK : %d\n",        __this->sfc_clk);
    log_debug("---SPI CLK : %d\n",        __this->spi_clk);
    log_debug("---HSB CLK : %d\n",        __this->hsb_clk);
    log_debug("---LSB CLK : %d\n",        __this->lsb_clk);
    log_debug("---P33 CLK : %d\n",        __this->p33_clk);

    log_debug("--USB CLK : %d\n",        clock_get_usb_freq());
    log_debug("--AUDIO CLK : %d\n",      clock_get_audio_freq());
    log_debug("--UART CLK : %d\n",       clock_get_uart_freq());
    log_debug("--BT CLK : %d\n",         clock_get_bt_freq());

    log_info("--480M OE : %d\n", (JL_CLOCK->PLL_CON1 & BIT(24)) ? 1 : 0);
    log_info("--320M OE : %d\n", (JL_CLOCK->PLL_CON1 & BIT(25)) ? 1 : 0);
    log_info("--192M OE : %d\n", (JL_CLOCK->PLL_CON1 & BIT(26)) ? 1 : 0);
    log_info("--137M OE : %d\n", (JL_CLOCK->PLL_CON1 & BIT(27)) ? 1 : 0);
    log_info("--107M OE : %d\n", (JL_CLOCK->PLL_CON1 & BIT(28)) ? 1 : 0);

    log_debug("---SYS DVDD : %d\n",  GET_SYSVDD_VOL_SEL());
    log_debug("---VDC13    : %d\n",       GET_VD13_HD_SEL());
    log_debug("---RANGE  : %d\n",         __this->range);
    log_debug("---TRIM SYS DVDD : %d\n",  __this->sys_dvdd_trim);

    log_info("SPI DIV  : %d\n",         JL_SFC->BAUD);
    log_debug("SFC_CON  : %08x\n",       JL_SFC->CON);
    log_debug("SFC_QCNT  : %08x\n",      JL_SFC->QUCNT);
    log_debug("CACHE_CON  : %04x\n",     JL_DSP->CON);
    log_info("SYS_DIV  : %08x\n",       JL_CLOCK->SYS_DIV);
    log_info("CLK_CON0 : %08x\n",       JL_CLOCK->CLK_CON0);
    log_info("CLK_CON1 : %08x\n",       JL_CLOCK->CLK_CON1);
    log_info("CLK_CON2 : %08x\n",       JL_CLOCK->CLK_CON2);
    log_info("CLK_CON3 : %08x\n",       JL_CLOCK->CLK_CON3);
    log_info("PLL_CON  : %08x\n",       JL_CLOCK->PLL_CON);
    log_info("PLL_CON1 : %08x\n",       JL_CLOCK->PLL_CON1);

    log_info("---GPCNTTABLE\n");
}

void core_voltage_dump(void)
{
    log_debug("---SYS DVDD : %d",  GET_SYSVDD_VOL_SEL());
    log_debug("---VDC13    : %d",       GET_VD13_HD_SEL());
    log_debug("---HSB CLK : %d",        __this->hsb_clk);
    log_debug("---SPI CLK : %d / SFC CLK %d",        __this->spi_clk, __this->sfc_clk);
    log_debug("---SFC_CON  : %08x / %d",       JL_SFC->CON, __this->spi_cs_cnt);
}

void clock_debug(void)
{
    u8 index = 0;
    u32 frequency = 0;

#if 1
    log_debug("---Switching 96M");
    hw_clock_switching(96000000L);
    clock_dump();

    log_debug("---Switching 48M");
    hw_clock_switching(48000000L);
    clock_dump();

    log_debug("---Switching 80M");
    hw_clock_switching(80000000L);
    clock_dump();

    log_debug("---Switching 120M");
    hw_clock_switching(120000000L);
    clock_dump();

    log_debug("---Switching 60M");
    hw_clock_switching(60000000L);
    clock_dump();

    log_debug("---Switching 160M");
    hw_clock_switching(160000000L);
    clock_dump();

    log_debug("---Switching 24M");
    hw_clock_switching(24000000L);
    clock_dump();

    log_debug("---Switching 160M");
    hw_clock_switching(160000000L);
    clock_dump();

    log_debug("---Switching 192M");
    hw_clock_switching(192000000L);
    clock_dump();

    log_debug("---Switching 24M");
    hw_clock_switching(24000000L);
    clock_dump();
#endif
}

void clock_init(u8 sys_in, u32 input_freq, u32 out_freq)
{
    hw_clock_init(sys_in, input_freq, out_freq);
}

void clock_switching(u32 out_freq)
{
    hw_clock_switching(out_freq);

    clock_dump();
}

void clock_tree_switching(u32 sfc_clk, u8 hsb_div, u8 lsb_div, u8 spi_div)
{
    hw_clock_tree_switching(sfc_clk, hsb_div, lsb_div, spi_div);

    clock_dump();
}

__attribute__((always_inline_when_const_args)) int clk_get(const char *name)
{
    if (!strcmp(name, "sys")) {
        return (int)clock_get_hsb_freq();
    }

    if (!strcmp(name, "lsb")) {
        return (int)clock_get_lsb_freq();
    }

    if (!strcmp(name, "uart")) {
        return (int)clock_get_uart_freq();
    }

    if (!strcmp(name, "timer")) {
        return (int)clock_get_iosc_freq();
    }

    if (!strcmp(name, "sd")) {
        return (int)clock_get_lsb_freq();
    }
    if (!strcmp(name, "spi")) {
        return (int)clock_get_lsb_freq();
    }
    if (!strcmp(name, "adc")) {
        return (int)clock_get_lsb_freq();
    }

    if (!strcmp(name, "mcpwm")) {
        return (int)clock_get_lsb_freq();
    }

    if (!strcmp(name, "plnk")) {
        return (int)clock_get_lsb_freq();
    }

    return -EINVAL;
}

int clk_set(const char *name, int clk)
{
    if (clk > 240 * 1000000L) {
        clk = 240 * 1000000L;
    }
    static int last_clk;

#ifdef CONFIG_FPGA_ENABLE
    return 0;
#endif

    if (!strcmp(name, "sys")) {
        if (clk != last_clk) {
            hw_clock_switching(clk);
            core_voltage_dump();
            last_clk = clk;
        }
        return 0;
    }

    if (!strcmp(name, "uart")) {
        return 0;
    }

    return -EINVAL;
}

int clk_set_sys_lock(int clk, int lock_en)
{
    return clk_set("sys", clk);
}

int clk_early_init(u8 sys_in, u32 input_freq, u32 out_freq)
{
    clock_init(sys_in, input_freq, out_freq);

    /* audio clk */
    JL_CLOCK->CLK_CON1 &= ~(BIT(2) | BIT(3));

    return 0;
}

int clk_late_init(void)
{
    return 0;
}

void sys_clk_set(u8 clk)
{
    CPU_CRITICAL_ENTER();

    if (clk == SYS_24M) {
        hw_clock_switching(24 * MHz_UNIT);
    } else if (clk == SYS_48M) {
        hw_clock_switching(48 * MHz_UNIT);
    }

    delay(10);

    CPU_CRITICAL_EXIT();
    clock_dump();
}

void clk_voltage_init(u8 mode, u8 sys_dvdd, u8 pwr_mode, u8 vdc13)
{
    if (mode == CLOCK_MODE_ADAPTIVE) {
        __this->voltage_adaptive = 1;

    } else {
        __this->sys_dvdd = sys_dvdd;
        __this->vdc13 = vdc13;
        __this->voltage_adaptive = 0;
    }
    __this->pwr_mode = pwr_mode;
    __this->cur_pwr_mode = PWR_LDO15;
}

void clk_init_osc_cap(u8 sel_l, u8 sel_r)
{
    __this->cap_l = sel_l;
    __this->cap_r = sel_r;
}

void clk_hcs12v_osc(u8 value)
{
    if (value > 0x1f) {
        value = 0x1f;
    }
    __this->hcs12v = value;
}

void clk_init_osc_ldos(u8 ldos)
{
    __this->ldos  = ldos;
}

void clk_set_osc_cap(u8 sel_l, u8 sel_r)
{

    if (sel_r > 0x0f) {
        sel_r = 0x0f;
    }

    if (sel_l > 0x0f) {
        sel_l = 0x0f;
    }

    log_info("\nreset cap %x %x\n", sel_l, sel_r);

    JL_ANA->WLA_CON8 &= ~(XOSC_CLS_12v_4(0xf) | XOSC_CRS_12v_4(0xf));
    JL_ANA->WLA_CON8 |= (XOSC_CLS_12v_4(sel_l) | XOSC_CRS_12v_4(sel_r));
}

void clk_set_default_osc_cap(void)
{
    clk_set_osc_cap(__this->cap_l, __this->cap_r);
}

u32 clk_get_osc_cap()
{
    u32 bt_osc_value = 0;

    bt_osc_value =  JL_ANA->WLA_CON8 & (XOSC_CLS_12v_4(0xf) | XOSC_CRS_12v_4(0xf));

    return (u32)(bt_osc_value >> 13);
}

/********************************************************************************/
/*
 *                   Debug API (测试程序)
 */
#define     OSC_IO_DEBUG_0(i, x)     //  {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT &= ~BIT(x);}
#define     OSC_IO_DEBUG_1(i, x)      // {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT |= BIT(x);}

#define GPCNT_SIZE  16

int gpcnt_avr[GPCNT_SIZE];

void osc_debug(void)
{
    int i;

    log_debug("CLK_CON0 : %08x",       JL_CLOCK->CLK_CON0);
    log_debug("CLK_CON1 : %08x",       JL_CLOCK->CLK_CON1);
    for (i = 0; i < ARRAY_SIZE(gpcnt_avr); i++) {
        log_debug("GPCNT - %d : 0x%x", i, gpcnt_avr[i]);
    }
}

void osc_wait_stable(void)
{
    int avr[GPCNT_SIZE];
    int i = 0;

    core_ring_enable(1);

    GPCNT_CLOCK_IN(GPCNT_CSS_LSB);

    do {
        // 设置闸门时间 = 32 * 2^16 / 16000000 =
        JL_GPCNT->CON = BIT(6);
        JL_GPCNT->CON |= 0b100 << 12;       // gating clock select
        JL_GPCNT->CON |= 0b001 << 1;        // counter clock select
        JL_GPCNT->CON |= 0b00110 << 8;        // gating time select

        JL_GPCNT->CON |= BIT(6);        // GPCNT clr pending
        JL_GPCNT->CON |= 1;             // GPCNT enable

        OSC_IO_DEBUG_1(A, 2);
        while (!(JL_GPCNT->CON & BIT(7)));
        OSC_IO_DEBUG_0(A, 2);

        // 获取晶振读数
        gpcnt_avr[i % GPCNT_SIZE] = JL_GPCNT->NUM;

        if (i >= GPCNT_SIZE) {
            break;
        }

        i++;
    } while (1);

    core_ring_enable(0);
}

static void _udelay(u32 usec)
{
    JL_TIMER0->CON = BIT(14);
    JL_TIMER0->CNT = 0;
    JL_TIMER0->PRD = 16 * 1000000L / 1000000L  * usec; // 1us
    JL_TIMER0->CON = BIT(0); // sys clk
    while ((JL_TIMER0->CON & BIT(15)) == 0);
    JL_TIMER0->CON = BIT(14);
}

void osc_fast_boot_test(void)
{
    log_debug("====Fast Boot=======");

    // Trim XOSC
    xosc_fsck_trim();
    log_debug("JL_ANA->WLA_CON8 : 0x%x", JL_ANA->WLA_CON8);
    log_debug("JL_ANA->WLA_CON9 : 0x%x", JL_ANA->WLA_CON9);

    // Auto switch OFF
    JL_P33->PMU_CON &= ~BIT(12);

    // Manual switch OFF
    SFR(JL_ANA->WLA_CON9, 4, 1, 0);

    // FSCKOE
    SFR(JL_ANA->WLA_CON9, 0, 1, 0);

    log_debug("JL_ANA->WLA_CON8 : 0x%x", JL_ANA->WLA_CON8);
    log_debug("JL_ANA->WLA_CON9 : 0x%x", JL_ANA->WLA_CON9);

    // Sel RC
    JL_CLOCK->CLK_CON0 |= (BIT(1) | BIT(0));
    JL_CLOCK->CLK_CON0 &= ~BIT(8);  // sel rc

    // Close bt osc
    SFR(JL_ANA->WLA_CON8, 0, 1, 0);
    _udelay(1000);

    u8 sys_div_bak = JL_CLOCK->SYS_DIV;
    JL_CLOCK->SYS_DIV = 0;

    OSC_IO_DEBUG_1(A, 2);

    // Open bt osc
    SFR(JL_ANA->WLA_CON8, 0, 1, 1);

    // Manual switch ON
    _udelay(20);
    OSC_IO_DEBUG_0(A, 2);

    // Manual switch OFF
    SFR(JL_ANA->WLA_CON9, 4, 1, 0);

    JL_CLOCK->SYS_DIV = sys_div_bak;

    JL_CLOCK->CLK_CON0 |= BIT(8);  // sel Pll

    log_debug("====Fast Boot End=======");
    while (1) {
    }
}

sec(.volatile_ram_code)
void switch_to_hrc(void)
{
    u32 delay_cnt = 100;
    RC_EN(1);

    RCH_EN(1);

    while (delay_cnt--) {
        asm("nop");
    }

    MAIN_CLOCK_SEL(MAIN_CLOCK_IN_RC);

    PLL_EN(0);
    JL_CLOCK->SYS_DIV = 0;
}
